package com.taotao.delayjob.example.conf.mvc;

import com.taotao.common.exception.BusinessException;
import com.taotao.common.model.RespData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 *
 * @author Fan Beibei
 * @since 1.0-SNAPSHOT
 */
@Slf4j
@RestControllerAdvice
public class SpringbootExceptionHandler {

    /**
     * 生产环境
     */
    private final static String ENV_PROD = "prod";

    /**
     * 当前环境
     */
    @Value("${spring.profiles.active}")
    private String profile;


//    ServletRequestBindingException
//     AsyncRequestTimeoutException


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public RespData<Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e1) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }


        if (e1.getMessage().contains("JSON parse error")) {
            return new RespData<Object>(RespData.FAIL_CODE, "参数JSON格式错误", null);
        } else {
            return new RespData<Object>(RespData.FAIL_CODE, "参数错误： " + e1.getMessage(), null);
        }

    }


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpMediaTypeNotAcceptableException.class, HttpMediaTypeNotSupportedException.class})
    public RespData<Object> handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException e1) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE, "cannot support MediaType,all support is " + e1.getSupportedMediaTypes(), null);
    }


    /**
     * 处理参数异常
     *
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MissingServletRequestPartException.class})
    public RespData<Object> handleMissingServletRequestPartException(MissingServletRequestPartException e1) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE, e1.getMessage(), null);
    }


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public RespData<Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e1) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE,
                " miss parameters,name=[" + e1.getParameterName() + "],type=[" + e1.getParameterType() + "]", null);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MissingPathVariableException.class})
    public RespData<Object> handleMissingPathVariableException(MissingPathVariableException e1) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE,
                " miss path variable parameters,name=[" + e1.getVariableName() + "]", null);
    }


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public RespData<Object> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e2) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<>(RespData.FAIL_CODE, "param type  error,name=[" + e2.getName() + "], required type is[" + e2.getRequiredType()
                + "],actual value is [" + e2.getValue() + "].\n", null);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({TypeMismatchException.class})
    public RespData<Object> handleTypeMismatchException(TypeMismatchException e2) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<>(RespData.FAIL_CODE, "property type  error,name=[" + e2.getPropertyName() + "], required type is[" + e2.getRequiredType()
                + "],actual value is [" + e2.getValue() + "].\n", null);
    }

    /**
     * 处理请求方式不支持异常
     *
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public RespData<Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE, "HTTP request type error!! required type is "
                + e.getSupportedHttpMethods() + ",but your type is " + e.getMethod(), null);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public RespData<Object> handleNoHandlerFoundException(NoHandlerFoundException e) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }

        return new RespData<Object>(RespData.FAIL_CODE,
                "404  url not found!!,request type is " + e.getHttpMethod() + ",url is " + e.getRequestURL(), null);
    }


    /**
     * 方法参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public RespData<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE, e.getBindingResult().getAllErrors().stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining("; ")), null);
    }


    // 校验异常
    @ExceptionHandler(ConstraintViolationException.class)
    public RespData<Object> bindException(ConstraintViolationException e) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE, e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining("; ")), null);
    }


    @ExceptionHandler(BindException.class)
    public RespData<Object> bindException(BindException e) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE, e.getAllErrors().stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining("; ")), null);
    }


    /**
     * 处理文件大小限制异常
     *
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MultipartException.class)
    public RespData<Object> handleMultipartException(MultipartException e) {
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        }
        return new RespData<Object>(RespData.FAIL_CODE, "upload file erorr!! " + ExceptionUtils.getStackTrace(e), null);
    }


    /**
     * 处理其他异常
     *
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Throwable.class)
    public RespData<Object> handlePlatformException(Throwable e) {
        //抛出的业务异常
        if (e instanceof BusinessException) {
            return new RespData<Object>(RespData.FAIL_CODE, e.getMessage(), null);
        }

        log.error("系统异常,{}", ExceptionUtils.getStackTrace(e));
        if (isProdEnv()) {
            return new RespData<Object>(RespData.FAIL_CODE, "系统异常,请联系管理员", null);
        } else {
            return new RespData<Object>(RespData.FAIL_CODE, ExceptionUtils.getStackTrace(e), null);
        }
    }


    private boolean isProdEnv() {
        return Objects.equals(ENV_PROD, profile);
    }
}
